use crate::ast::*;

// lalrpop convention
grammar;

match {
  // skip white spaces and comments
  r"\s*" => {},
  r"//[^\n\r]*[\n\r]*" => {},
  r"/\*[\s\S]*\*/" => {},
  // the left condition remains default
  _
}

// define CompUnit
// @return: String[FuncDef]
pub CompUnit: CompUnit = <func_def: FuncDef> => CompUnit { <> };

// define FuncDef
// @return: String
FuncDef: FuncDef = {
  <func_type: FuncType> <ident: Ident> "(" ")" <block: Block> => {
    FuncDef { <> }
  }
};

FuncType: FuncType = {
  "int" => FuncType::Int
};

Block: Block = {
  "{" <stmt: Stmt> "}" => Block { <> }
};

Stmt: Stmt = {
  "return" <exp: Exp> ";" => Stmt { <> }
};

Exp: Exp = <unary_exp: UnaryExp> => Exp { unary_exp: Box::new(<>) };

UnaryExp: UnaryExp = {
  <primary_exp: PrimaryExp> => UnaryExp::PrimaryExp(<>),
  <unary_op: UnaryOp> <unary_exp: UnaryExp> => {
    UnaryExp::MUnaryExp{ unary_op, unary_exp: Box::new(unary_exp) }
  }
};

PrimaryExp: PrimaryExp = {
  "(" <exp: Exp> ")" => PrimaryExp::Exp(<>),
  <num: Number> => PrimaryExp::Number(<>)
};

UnaryOp: UnaryOp = {
  "+" => UnaryOp::Positive,
  "-" => UnaryOp::Negative,
  "!" => UnaryOp::LogicNot,
};

Number: i32 = {
  <num: IntConst> => <>
};

// match rule:
// leading with '_' or 52 alphabets
// along with 10 digits after first position
Ident: String = r"[_a-zA-Z][_a-zA-Z0-9]*" => <>.to_string();

IntConst: i32 = {
  r"[1-9][0-9]*"       => i32::from_str_radix(<>, 10).unwrap(),
  r"0[0-7]*"           => i32::from_str_radix(<>, 8).unwrap(),
  r"0[xX][0-9a-fA-F]+" => i32::from_str_radix(&<>[2..], 16).unwrap(),
};

/* vim: set ft=rust : */
